import { Injectable, Logger } from '@nestjs/common';
import * as Minio from 'minio';
import { Readable } from 'stream';

// Minio 配置接口
interface MinioConfig {
  endpoint: string;
  accessKey: string;
  secretKey: string;
  bucket: string;
  secure: boolean;
  callbackUrl: string;
  callbackMongoUrl: string;
}

// 从环境变量读取配置
const storageType = process.env.STORAGE_TYPE || 'qiniu';

const minioConfig: MinioConfig = {
  endpoint: process.env.MINIO_ENDPOINT || 'localhost:9000',
  accessKey: process.env.MINIO_ACCESS_KEY || 'minioadmin',
  secretKey: process.env.MINIO_SECRET_KEY || 'minioadmin',
  bucket: process.env.MINIO_BUCKET || 'appthen',
  secure: process.env.MINIO_SECURE === 'true',
  callbackUrl: '',
  callbackMongoUrl: '',
};

const cdnDomain = process.env.CDN_DOMAIN || 'cdn.appthen.com';

@Injectable()
export class MinioService {
  private client: Minio.Client;
  private readonly logger = new Logger(MinioService.name);
  private readonly isEnabled = storageType === 'minio';

  constructor() {
    if (this.isEnabled) {
      this.client = new Minio.Client({
        endPoint: minioConfig.endpoint.split(':')[0] || 'localhost',
        port: parseInt(minioConfig.endpoint.split(':')[1] || '9000'),
        useSSL: minioConfig.secure,
        accessKey: minioConfig.accessKey,
        secretKey: minioConfig.secretKey,
      });

      this.initializeBucket();
    }
  }

  /**
   * 初始化存储桶
   */
  private async initializeBucket(): Promise<void> {
    try {
      const bucketExists = await this.client.bucketExists(minioConfig.bucket);
      if (!bucketExists) {
        await this.client.makeBucket(minioConfig.bucket, 'us-east-1');
        this.logger.log(`Minio bucket "${minioConfig.bucket}" created successfully`);
      } else {
        this.logger.log(`Minio bucket "${minioConfig.bucket}" already exists`);
      }
    } catch (error) {
      this.logger.error('Failed to initialize Minio bucket:', error);
    }
  }

  /**
   * 检查服务是否可用
   */
  isAvailable(): boolean {
    return this.isEnabled;
  }

  /**
   * 上传文件到Minio
   */
  async uploadFile(
    fileBuffer: Buffer,
    fileName: string,
    contentType: string = 'application/octet-stream'
  ): Promise<{ url: string; key: string }> {
    if (!this.isEnabled) {
      throw new Error('Minio service is not enabled');
    }

    try {
      // 生成唯一的文件名
      const timestamp = Date.now();
      const randomString = Math.random().toString(36).substring(2, 8);
      const key = `project-exports/${timestamp}_${randomString}_${fileName}`;

      // 创建可读流
      const stream = new Readable();
      stream.push(fileBuffer);
      stream.push(null);

      // 上传文件
      await this.client.putObject(minioConfig.bucket, key, stream, fileBuffer.length, {
        'Content-Type': contentType,
        'Cache-Control': 'public, max-age=31536000', // 1年缓存
      });

      // 构建访问URL
      const protocol = minioConfig.secure ? 'https' : 'http';
      const endpoint = minioConfig.endpoint.includes(':')
        ? minioConfig.endpoint
        : `${minioConfig.endpoint}:${minioConfig.secure ? 443 : 9000}`;

      const url = `${protocol}://${endpoint}/${minioConfig.bucket}/${key}`;

      this.logger.log(`File uploaded successfully: ${fileName} -> ${url}`);

      return { url, key };
    } catch (error) {
      this.logger.error(`Failed to upload file ${fileName}:`, error);
      throw new Error(`Minio upload failed: ${error.message}`);
    }
  }

  /**
   * 上传项目导出ZIP文件
   */
  async uploadProjectExport(
    zipBuffer: Buffer,
    projectId: string,
    fileName?: string
  ): Promise<{ url: string; key: string }> {
    const exportFileName = fileName || `project-${projectId}-export.zip`;
    return this.uploadFile(zipBuffer, exportFileName, 'application/zip');
  }

  /**
   * 上传项目资源文件
   */
  async uploadProjectResource(
    fileBuffer: Buffer,
    originalUrl: string,
    projectId: string
  ): Promise<{ url: string; key: string }> {
    // 从原始URL提取文件名和扩展名，保持路径结构
    let resourcePath = '';
    if (originalUrl.startsWith('http://') || originalUrl.startsWith('https://')) {
      // 完整URL：提取路径部分，去掉域名
      const urlObj = new URL(originalUrl);
      resourcePath = urlObj.pathname.startsWith('/') ? urlObj.pathname.substring(1) : urlObj.pathname;
    } else if (originalUrl.startsWith('/')) {
      // 相对路径：去掉开头的 /
      resourcePath = originalUrl.substring(1);
    } else {
      // 纯hash或其他：直接使用
      resourcePath = originalUrl;
    }

    // 检测内容类型（传入文件buffer以便更准确检测）
    const contentType = this.detectContentType(resourcePath, fileBuffer);

    const key = resourcePath;

    if (!this.isEnabled) {
      throw new Error('Minio service is not enabled');
    }

    try {
      const stream = new Readable();
      stream.push(fileBuffer);
      stream.push(null);

      await this.client.putObject(minioConfig.bucket, key, stream, fileBuffer.length, {
        'Content-Type': contentType,
        'Cache-Control': 'public, max-age=31536000',
        'X-Original-Url': originalUrl,
        'X-Project-Id': projectId,
      });

      const protocol = minioConfig.secure ? 'https' : 'http';
      const endpoint = minioConfig.endpoint.includes(':')
        ? minioConfig.endpoint
        : `${minioConfig.endpoint}:${minioConfig.secure ? 443 : 9000}`;

      const url = `${protocol}://${endpoint}/${minioConfig.bucket}/${key}`;

      this.logger.log(`Resource uploaded: ${originalUrl} -> ${url}`);

      return { url, key };
    } catch (error) {
      this.logger.error(`Failed to upload resource ${originalUrl}:`, error);
      throw new Error(`Minio resource upload failed: ${error.message}`);
    }
  }

  /**
   * 批量上传项目资源
   */
  async uploadProjectResources(
    resources: Array<{
      buffer: Buffer;
      originalUrl: string;
      fileName: string;
    }>,
    projectId: string
  ): Promise<Array<{ originalUrl: string; url: string; key: string }>> {
    const results = [];

    for (const resource of resources) {
      try {
        const result = await this.uploadProjectResource(
          resource.buffer,
          resource.originalUrl,
          projectId
        );
        results.push({
          originalUrl: resource.originalUrl,
          url: result.url,
          key: result.key,
        });
      } catch (error) {
        this.logger.error(`Failed to upload resource ${resource.originalUrl}:`, error);
        results.push({
          originalUrl: resource.originalUrl,
          url: '',
          key: '',
          error: error.message,
        });
      }
    }

    return results;
  }

  /**
   * 删除文件
   */
  async deleteFile(key: string): Promise<void> {
    if (!this.isEnabled) {
      throw new Error('Minio service is not enabled');
    }

    try {
      await this.client.removeObject(minioConfig.bucket, key);
      this.logger.log(`File deleted successfully: ${key}`);
    } catch (error) {
      this.logger.error(`Failed to delete file ${key}:`, error);
      throw new Error(`Minio delete failed: ${error.message}`);
    }
  }

  /**
   * 检查文件是否存在
   */
  async fileExists(key: string): Promise<boolean> {
    if (!this.isEnabled) {
      return false;
    }

    try {
      await this.client.statObject(minioConfig.bucket, key);
      return true;
    } catch (error) {
      return false;
    }
  }

  /**
   * 获取文件信息
   */
  async getFileInfo(key: string): Promise<Minio.BucketItem & { contentType?: string }> {
    if (!this.isEnabled) {
      throw new Error('Minio service is not enabled');
    }

    try {
      const statResult = await this.client.statObject(minioConfig.bucket, key);
      // 转换为 BucketItem 格式，并添加额外属性
      return {
        name: key,
        size: statResult.size,
        etag: statResult.etag,
        lastModified: statResult.lastModified,
        contentType: (statResult as any).contentType,
        // 添加其他可能需要的属性
        ...statResult
      };
    } catch (error) {
      this.logger.error(`Failed to get file info ${key}:`, error);
      throw new Error(`Minio stat failed: ${error.message}`);
    }
  }

  /**
   * 生成预签名URL（用于直接下载）
   */
  async getPresignedUrl(key: string, expiry: number = 3600): Promise<string> {
    if (!this.isEnabled) {
      throw new Error('Minio service is not enabled');
    }

    try {
      return await this.client.presignedGetObject(minioConfig.bucket, key, expiry);
    } catch (error) {
      this.logger.error(`Failed to generate presigned URL for ${key}:`, error);
      throw new Error(`Minio presigned URL failed: ${error.message}`);
    }
  }

  /**
   * 列出项目资源
   */
  async listProjectResources(projectId: string): Promise<Minio.BucketItem[]> {
    if (!this.isEnabled) {
      return [];
    }

    try {
      const stream = this.client.listObjects(minioConfig.bucket, '', true);
      const objects: Minio.BucketItem[] = [];

      return new Promise((resolve, reject) => {
        stream.on('data', (obj: any) => {
          // 转换为 BucketItem 格式
          if (obj.name) {
            objects.push({
              name: obj.name,
              size: obj.size || 0,
              etag: obj.etag || '',
              lastModified: obj.lastModified || new Date(),
            });
          }
        });
        stream.on('error', reject);
        stream.on('end', () => resolve(objects));
      });
    } catch (error) {
      this.logger.error(`Failed to list resources for project ${projectId}:`, error);
      return [];
    }
  }

  /**
   * 删除项目相关文件
   */
  async deleteProjectFiles(projectId: string): Promise<void> {
    if (!this.isEnabled) {
      return;
    }

    try {
      const objects = await this.listProjectResources(projectId);
      const objectsToDelete = objects.map(obj => obj.name).filter(Boolean);

      if (objectsToDelete.length > 0) {
        await this.client.removeObjects(minioConfig.bucket, objectsToDelete);
        this.logger.log(`Deleted ${objectsToDelete.length} files for project ${projectId}`);
      }
    } catch (error) {
      this.logger.error(`Failed to delete project files ${projectId}:`, error);
    }
  }

  /**
   * 检测文件内容类型
   */
  private detectContentType(fileName: string, fileBuffer?: Buffer): string {
    const extension = fileName.toLowerCase().split('.').pop();

    const contentTypes: Record<string, string> = {
      'jpg': 'image/jpeg',
      'jpeg': 'image/jpeg',
      'png': 'image/png',
      'gif': 'image/gif',
      'webp': 'image/webp',
      'svg': 'image/svg+xml',
      'css': 'text/css',
      'js': 'application/javascript',
      'mjs': 'application/javascript',
      'json': 'application/json',
      'html': 'text/html',
      'htm': 'text/html',
      'xml': 'application/xml',
      'pdf': 'application/pdf',
      'zip': 'application/zip',
      'mp4': 'video/mp4',
      'webm': 'video/webm',
      'mp3': 'audio/mpeg',
      'wav': 'audio/wav',
      'ogg': 'audio/ogg',
      'woff': 'font/woff',
      'woff2': 'font/woff2',
      'ttf': 'font/ttf',
      'eot': 'application/vnd.ms-fontobject',
    };

    // 如果有扩展名，直接返回对应的类型
    if (extension && contentTypes[extension]) {
      return contentTypes[extension];
    }

    // 如果没有扩展名（如纯hash），通过文件头检测
    if (fileBuffer && fileBuffer.length > 0) {
      return this.detectContentTypeFromBuffer(fileBuffer);
    }

    // 对于纯hash且没有文件头信息的情况，默认使用application/octet-stream
    return 'application/octet-stream';
  }

  /**
   * 从文件头检测内容类型
   */
  private detectContentTypeFromBuffer(buffer: Buffer): string {
    // 检查常见的文件头标识
    if (buffer.length < 4) return 'application/octet-stream';

    // PNG
    if (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4E && buffer[3] === 0x47) {
      return 'image/png';
    }
    // JPEG
    if (buffer[0] === 0xFF && buffer[1] === 0xD8 && buffer[2] === 0xFF) {
      return 'image/jpeg';
    }
    // GIF
    if (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46) {
      return 'image/gif';
    }
    // WebP
    if (buffer[8] === 0x57 && buffer[9] === 0x45 && buffer[10] === 0x42 && buffer[11] === 0x50) {
      return 'image/webp';
    }
    // PDF
    if (buffer[0] === 0x25 && buffer[1] === 0x50 && buffer[2] === 0x44 && buffer[3] === 0x46) {
      return 'application/pdf';
    }
    // ZIP
    if (buffer[0] === 0x50 && buffer[1] === 0x4B && (buffer[2] === 0x03 || buffer[2] === 0x05 || buffer[2] === 0x07)) {
      return 'application/zip';
    }

    return 'application/octet-stream';
  }

  /**
   * 获取存储桶信息
   */
  async getBucketInfo(): Promise<{ exists: boolean; size?: number; objectCount?: number }> {
    if (!this.isEnabled) {
      return { exists: false };
    }

    try {
      const exists = await this.client.bucketExists(minioConfig.bucket);
      if (!exists) {
        return { exists: false };
      }

      // 获取存储桶统计信息（需要遍历所有对象，可能较慢）
      let size = 0;
      let objectCount = 0;
      const stream = this.client.listObjects(minioConfig.bucket, '', true);

      return new Promise((resolve) => {
        stream.on('data', (obj: any) => {
          size += obj.size || 0;
          objectCount++;
        });
        stream.on('error', () => resolve({ exists: true }));
        stream.on('end', () => resolve({ exists: true, size, objectCount }));
      });
    } catch (error) {
      this.logger.error('Failed to get bucket info:', error);
      return { exists: false };
    }
  }
}